package com.example.lottery_system.service.impl;

import com.example.lottery_system.common.errcode.ServiceErrorCodeConstant;
import com.example.lottery_system.common.utils.JacksonUtil;
import com.example.lottery_system.common.utils.RedisUtil;
import com.example.lottery_system.controller.param.DrawPrizeParam;
import com.example.lottery_system.controller.param.Winner;
import com.example.lottery_system.controller.param.WinningRecordParam;
import com.example.lottery_system.dao.DataObject.*;
import com.example.lottery_system.dao.Mapper.*;
import com.example.lottery_system.service.DrawPrizeService;
import com.example.lottery_system.service.Enum.ActivityPrizeStatusEnum;
import com.example.lottery_system.service.Enum.ActivityPrizeTiersEnum;
import com.example.lottery_system.service.Enum.ActivityStatusEnum;
import com.example.lottery_system.service.dto.WinningRecordsDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.lottery_system.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lottery_system.common.config.DirectRabbitConfig.ROUTING;

@Service
@Slf4j
public class DrawPrizeServiceImpl implements DrawPrizeService {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    ActivityMapper activityMapper;

    @Autowired
    ActivityPrizeMapper activityPrizeMapper;


    @Autowired
    PrizeMapper prizeMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    WinningRecordMapper winningRecordMapper;

    @Autowired
    RedisUtil redisUtil;

    private static final String  SAVE_WINNING_RECODE_PREV = "SAVE_WINNING_RECODE_";

    private static final Long SAVE_WINNING_RECODE_OUT_TIME = 3 * 24 * 60 * 60L;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        // 异步处理
        Map<String,String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        log.info("异步生成消息成功!,map:{}",JacksonUtil.writeValueAsString(map));
    }

    @Override
    public boolean checkDrawPrizeParam(DrawPrizeParam param) {
        // 检查活动
        ActivityDO activityDo = activityMapper.selectByActivityId(param.getActivityId());
        // 查询奖品
        ActivityPrizeDO activityPrizeDo = activityPrizeMapper.selectByPrizeId(param.getActivityId(),param.getPrizeId());

        if(null == activityDo|| null == activityPrizeDo ) {
            log.warn("checkDrawPrizeParam：{}", ServiceErrorCodeConstant.DRAW_ACTIVITY_IS_EMPTY.getMsg());
            return false;
        }

        // 检查活动是否已经完成
        if (!activityDo.getStatus().equalsIgnoreCase(ActivityStatusEnum.RUNNING.name())) {
            log.warn("checkDrawPrizeParam:{}",ServiceErrorCodeConstant.DRAW_ACTIVITY_FINISHED);
            return false;
        }

        // 检查奖品是否已经被完成
        if (!activityPrizeDo.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.RUNNING.name())) {
            log.warn("checkDrawPrizeParam:{}",ServiceErrorCodeConstant.DRAW_ACTIVITY_PRIZE_FINISHED);
            return false;
        }

        // 检查中奖人数 < 奖品数
        if (param.getWinnerList().size() > activityPrizeDo.getPrizeAmount()) {
            log.warn("checkDrawPrizeParam：{}",ServiceErrorCodeConstant.DRAW_ACTIVITY_PRIZE_USERS_ERROR);
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveRecord(DrawPrizeParam param) {

        /**
         * 查询活动信息
         */
        ActivityDO activityDO=  activityMapper.selectByActivityId(param.getActivityId());

        /**
         * 查询奖品信息
         */
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByPrizeId(param.getActivityId(), param.getPrizeId());
        PrizeDO prizeDO =  prizeMapper.selectById(param.getPrizeId());

        /**
         * 查询用户信息
         *
         */
        List<UserDO> userDOList = userMapper.selectDOByUserIds(param.getWinnerList()
                .stream()
                .map(Winner::getUserId)
                .collect(Collectors.toList()));


        /**
         * 整合以奖品为主键的中奖记录
         */

        List<WinningRecordDO> winningRecordDOList = userDOList.stream().map(userDO -> new WinningRecordDO(
                param.getActivityId(),
                activityDO.getActivityName(),
                param.getPrizeId(),
                prizeDO.getName(),
                activityPrizeDO.getPrizeTiers(),
                userDO.getId(),
                userDO.getUserName(),
                userDO.getEmail(),
                userDO.getPhoneNumber(),
                param.getWinningTime()
        )).collect(Collectors.toList());

        /**
         * 先插入数据库保存
         */
        winningRecordMapper.insertDoList(winningRecordDOList);

        /**
         * 保存以奖品为主键的中奖记录
          */
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId() , winningRecordDOList, SAVE_WINNING_RECODE_OUT_TIME);


        /**
         * 保存以活动为主键的中奖记录
         */
       List<WinningRecordDO> winningRecordDOS = winningRecordMapper.selectDoByActivity(param.getActivityId(),null);
       if (CollectionUtils.isEmpty(winningRecordDOS)) {
           log.error("从活动id中获取中奖记录失败!");
           return new ArrayList<>();
       }
       cacheWinningRecords(param.getActivityId() + "" , winningRecordDOS, SAVE_WINNING_RECODE_OUT_TIME);

        return winningRecordDOList;
    }

    @Override
    public List<WinningRecordsDto> showWinningRecord(WinningRecordParam param) {
        List<WinningRecordsDto> winningRecordsDtoList = null;



//        如果缓存中没有，那么我们就需要从数据库中查询
        List<WinningRecordDO> winningRecordDOList = getWinningRecord(param);

         winningRecordsDtoList = winningRecordDOList.stream().map(winningRecordDO -> new WinningRecordsDto(
                winningRecordDO.getWinnerId(),
                winningRecordDO.getWinnerName(),
                winningRecordDO.getPrizeName(),
                ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()),
                winningRecordDO.getWinningTime()
        )).collect(Collectors.toList());

        return winningRecordsDtoList;

    }

    private List<WinningRecordDO> getWinningRecord(WinningRecordParam param) {
        // 看缓存中是否存在该数据
        //        查询缓存中有的数据
        String key = SAVE_WINNING_RECODE_PREV + param.getActivityId()
                + (null == param.getPrizeId() ? "" : "_" + param.getPrizeId());
        if (redisUtil.contains(key)) {

            List<WinningRecordDO> winningRecordDOList = getRecordByRedis(key);

            return winningRecordDOList;
        }

//        如果缓存中没有该数据，就查询数据库
        return winningRecordMapper.selectDoByActivity(param.getActivityId(), param.getPrizeId());
    }


    @Override
    public void deleteWinningReCord(Long activityId) {
        if (null == activityId) {
            log.warn("deleteWinningReCord::key为空!");
            return;
        }

        // 首先判断先删除活动中奖记录
        String str = SAVE_WINNING_RECODE_PREV + activityId;
        if (redisUtil.contains(str)) {
            redisUtil.delete(str);
        }

//        最后删除活动奖品中奖记录
        // 先获取所以该活动的奖品id
        WinningRecordParam winningRecordParam = new WinningRecordParam();
        winningRecordParam.setActivityId(activityId);
        List<WinningRecordDO> winningRecord = getWinningRecord(winningRecordParam);
        List<Long> prizeIds = winningRecord.stream().map(WinningRecordDO::getPrizeId).collect(Collectors.toList());

        for (Long prizeId:prizeIds) {
            String key = SAVE_WINNING_RECODE_PREV+activityId + "_" +  prizeId;
            if (redisUtil.contains(key)) {
                redisUtil.delete(key);
            }
        }


    }


    /**
     *
     * 保存中奖记录
     *
     * @param key
     * @param winningRecordDOList
     * @param outTime
     */

    private void cacheWinningRecords(String key, List<WinningRecordDO> winningRecordDOList, Long outTime) {

        if (!StringUtils.hasText(key)) {
            log.warn("cacheWinningRecords::key{}", key);
            return;
        }

        try {
            if (!key.contains(SAVE_WINNING_RECODE_PREV)) {
                 key =  SAVE_WINNING_RECODE_PREV + key;
            }
            String str = JacksonUtil.writeValueAsString(winningRecordDOList);
            redisUtil.put(key,str,outTime);
        } catch (Exception e) {
            log.error("中奖记录存入缓存失败!");
        }
    }


    private List<WinningRecordDO> getRecordByRedis(String key) {
        if (!StringUtils.hasText(key)) {
            log.error("getRecordByRedis中的Key为空!");
            return List.of();
        }


        // 获取中奖记录
        try {
            String value = redisUtil.get(key);
            List<WinningRecordDO> winningRecordDOList = JacksonUtil.readListValue(value, WinningRecordDO.class);
            return winningRecordDOList;
        } catch (Exception e) {
            log.error("从缓存中getRecordByRedis失败key: ",key,e);
            return List.of();
        }


    }


}
